package com.sunbim.rsrv.common.exception;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.sunbim.rsrv.common.response.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.dao.DataAccessException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.support.MissingServletRequestPartException;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 全局异常处理
 */
@Slf4j
@RestControllerAdvice
public class ErrorHandler {

    // Satoken异常
    @ExceptionHandler(SaTokenException.class)
    public R authenticationException(HttpServletRequest request, Exception e) {
        logfunc(request, e.getMessage());
        // 登录认证异常
        if (e instanceof NotLoginException) return r(Error.AUTHEN_ERROR);
        // 权限校验异常
        if (e instanceof NotRoleException) {
            NotRoleException ee = (NotRoleException) e;
            return r(Error.AUTHOR_ERROR, "需要 " + ee.getRole() + " 权限");
        }
        return r(Error.AUTHOR_ERROR, e.getMessage());
    }

    // 自定义请求异常
    @ExceptionHandler(RequestException.class)
    public R handlerRequestException(HttpServletRequest request, Exception e) {
        logfunc(request, e.getMessage());
        log.error("发生异常：{}",e);
        return r(Error.REQUEST_DATA_ERROR, e.getMessage());
    }

    // IO异常
    @ExceptionHandler(IOException.class)
    public R handlerIOException(HttpServletRequest request, Exception e) {
        logfunc(request, e.getMessage());
        return r(Error.SERVICE_ERROR, "IO流异常");
    }

    // @Deprecated: 捕捉不到,废弃
    // @ExceptionHandler(HttpClientErrorException.class)
    // public R handlerHttpClientErrorException(HttpServletRequest request, HttpClientErrorException e) {
    //     logfunc(request, e.getMessage());
    //     int status = e.getStatusCode().value();
    //     if (status == 404) {
    //         return R.error(404, "请求路径不存在");
    //     }
    //     return r(Error.REQUEST_ERROR);
    // }


    // json参数校验异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R handlerMethodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) {
        // 获取所有异常
        List<ObjectError> errors = e.getBindingResult().getAllErrors();
        // 使用 stream().map().collect 封装为字符串
        String msg = errors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining("; "));
        String formatStr = StrUtil.format("参数异常: {}", msg);
        logfunc(request, formatStr);
        return r(Error.REQUEST_DATA_ERROR, formatStr);
    }

    // form参数校验异常
    @ExceptionHandler(BindException.class)
    public R handleBindException(HttpServletRequest request, BindException e) {
        List<ObjectError> errors = e.getBindingResult().getAllErrors();
        // 使用 stream().map().collect 封装为字符串
        String msg = errors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining("; "));
        String formatStr = StrUtil.format("参数异常: {}", msg);
        logfunc(request, formatStr);
        return r(Error.REQUEST_DATA_ERROR, formatStr);
    }

    // form-data 参数异常
    @ExceptionHandler(MissingServletRequestPartException.class)
    public R handleMissingServletRequestPartException(HttpServletRequest request, MissingServletRequestPartException e) {
        String requestPartName = e.getRequestPartName();
        String formatStr = StrUtil.format("缺少参数: {}", requestPartName);
        logfunc(request, formatStr);
        return r(Error.REQUEST_DATA_ERROR, formatStr);
    }

    // get方法的query参数异常
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public R handleMissingServletRequestParameterException(HttpServletRequest request, MissingServletRequestParameterException e) {
        String parameterName = e.getParameterName();
        String formatStr = StrUtil.format("缺少参数: {}", parameterName);
        logfunc(request, formatStr);
        return r(Error.REQUEST_DATA_ERROR, formatStr);
    }

    // 请求方法异常
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R handlerHttpRequestMethodNotSupportedException(HttpServletRequest request, HttpRequestMethodNotSupportedException e) {
        logfunc(request, e.getMessage());
        return r(Error.REQUEST_DATA_ERROR, "不支持的请求方法: " + e.getMethod());
    }

    // 请求类型异常
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public R handlerHttpMediaTypeNotSupportedException(HttpServletRequest request, HttpMediaTypeNotSupportedException e) {
        logfunc(request, e.getMessage());
        String contentTypeAll = e.getContentType().toString();
        String contentType = contentTypeAll.split(";")[0];
        return r(Error.REQUEST_DATA_ERROR, "不支持的请求类型: " + contentType);
    }

    // json序列化异常
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public R handlerHttpMessageNotReadableException(HttpServletRequest request, HttpMessageNotReadableException e) {
        logfunc(request, e.getMessage());
        String regex = "\\\"(.*?)\\\"";
        String matchStr = ReUtil.findAll(regex, e.getMessage(), 0).get(0).trim().replace("\"", "");
        return r(Error.REQUEST_DATA_ERROR, "json序列化异常,请确定参数有效: " + matchStr);
    }

    // sql异常
    @ExceptionHandler(DataAccessException.class)
    public R handlerDataAccessException(HttpServletRequest request, Exception e) {
        SQLException se = (SQLException) e.getCause();
        // int errorCode = se.getErrorCode();
        logfunc(request, se.getMessage());
        return r(Error.SQL_ERROR);
    }

    // 自定义业务异常
    @ExceptionHandler(ServiceException.class)
    public R serviceException(HttpServletRequest request, Exception e) {
        logfunc(request, e.getMessage());
        return r(Error.SERVICE_ERROR, e.getMessage());
    }


    // 自定义msg的error返回结果
    private R r(Error error, String msg) {
        return R.error(error, msg);
    }

    // 默认msg的error返回结果
    private R r(Error error) {
        return R.error(error);
    }

    // 用于生成统一的日志格式
    private void logfunc(HttpServletRequest request, String msg) {
        log.error("[host:{}] [Method:{}] [URL:{}]", request.getRemoteHost(), request.getMethod(), request.getRequestURL());
        log.error("[msg:{}]", msg);
    }
}
